package com.zzr.demo.sso.client01.web;

import com.sun.java.browser.plugin2.DOM;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.print.Pageable;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: xiaomi
 * @date: 2021/8/22
 * @description:
 */
@Controller
@Slf4j
public class IndexController {

    public static final String TOKEN_KEY = "client_token_session";
    /**
     * 1. localhost
     * 2. oss.server.com
     */
    public static final String DOMAIN_NAME = "oss.server.com";
    /**
     * 存放 client token 的 map
     * client token - 时间戳
     */
    public static final Map<String, Long> tokenMap = new HashMap<>();


    @GetMapping("/")
    public String indexV1(@RequestParam(value = "token", required = false) String token, HttpSession session,
                          HttpServletRequest request, @CookieValue(value = "client_token", required = false) String client_token,
                          HttpServletResponse response) throws IOException {
        //1、验证本系统凭证 client_token
        if (!StringUtils.isEmpty(client_token)) {
            //去 session 进行验证 - tokenMap
            //String serverToken = ((String) session.getAttribute(TOKEN_KEY));
//            if (client_token.equals(serverToken)) {
//                //如果相等 - NOTEME 由于 session 设置了有效期（应该由 sso.server 获得，此处为了简便直接在 client 设置了）
//                return "index";
//            }
            if (tokenMap.containsKey(client_token)) {
                //再去验证时间戳
                if (System.currentTimeMillis() < tokenMap.get(client_token)) {
                    //在有效期内
                    return "index";
                }
            }
        }
        //没有 cookie || session token 失效了
        //2、验证 sso.server 的凭证
        if (StringUtils.isEmpty(token)) {
            //如果同样木有 server token,就去验证中心进行验证
            return "redirect:http://" + DOMAIN_NAME + ":8080/login.html?callback=" + request.getRequestURL();
        }
        //3、验证 sever.token
        String userName = getUserName(token);
        if (StringUtils.isEmpty(userName)) {
            //3.1 没有获取到用户信息（失效的 token）
            //重新登录
            return "redirect:http://" + DOMAIN_NAME + ":8080/login.html?callback=" + request.getRequestURL();
        }
        //给 model 进行展示
        session.setAttribute("loginUser", userName);
        //给本域设置 cookie 
        Cookie cookie = new Cookie("client_token", token);
        response.addCookie(cookie);


        //NOTEME 不能设置 session！因为是跟请求头的 JSESSIONID 绑定的，所以应该将 token 放在缓存，并设置过期时间。
        //简易版，直接放在本地 Map 里
        //session.setAttribute(TOKEN_KEY, token);
        //session.setMaxInactiveInterval(300);
        tokenMap.put(token, System.currentTimeMillis() + 60000);

        return "index";
    }

    @GetMapping("/test")
    public String indexVTest(@RequestParam(value = "token", required = false) String token, HttpSession session,
                             HttpServletRequest request, @CookieValue(value = "sso_token", required = false) String sso_token) throws IOException {
        //1.判断是否存在 session
        //NOTEME 在 client 登录成功之后，是不应该存 session 的：因为 session 存在服务端,如果设置过期时间也是同一设置的（我的方案：获取 cookie 对比session ）
        String sysTokenStr = (String) session.getAttribute(TOKEN_KEY);
        //TODO:去本地服务器查询 token 是否过期
        //checkToken
        if (!StringUtils.isEmpty(sysTokenStr)) {
            //信任本系统
            //token 没有过期
            return "index";
        }
        if (StringUtils.isEmpty(sso_token)) {
            //说明之前没有登录 -- NOTEME 错了，由于 sso_token 是 oss.server.com 的域名的，所以此处是绝对不会拿到的！只能根据 token 来进行判断！
            //2.拿到 token
            if (StringUtils.isEmpty(token)) {
                //2.1 没有 token 就跳转到 SSO server
                //NOTEME 由于本次跳转还带上了来源连接，其实本次跳转的目的有两个：1.让用户进行手动登录；2.去 oss.server 判断是否已经登录（根据同个浏览器绑定的 sso.server.com 的 sso_token ）
                return "redirect:http://" + DOMAIN_NAME + ":8080/login.html?callback=" + request.getRequestURL();
            }
            //2.2 token 有值，去 server 进行校验
            String userName = getUserName(token);
            if (StringUtils.isEmpty(userName)) {
                //2.2.1 没有获取到用户信息（失效的 token）
                //重新登录
                return "redirect:http://" + DOMAIN_NAME + ":8080/login.html?callback=" + request.getRequestURL();
            }
            //拿到有效用户信息
            session.setAttribute("loginUser", userName);
            //给本域设置 session
            session.setAttribute(TOKEN_KEY, token);
        } else {
            //session 存在，再去获取 username 
            String userName = getUserName(sso_token);
            if (StringUtils.isEmpty(userName)) {
                //2.2.1 没有获取到用户信息（失效的 token）
                //重新登录
                return "redirect:http://" + DOMAIN_NAME + ":8080/login.html?callback=" + request.getRequestURL();
            }
            //拿到有效用户信息
            session.setAttribute("loginUser", userName);
            //给本域设置 session
            session.setAttribute(TOKEN_KEY, token);
        }


        return "index";
    }


    /**
     * 退出登录
     *
     * @return
     */
    @PostMapping("/logout")
    public String logout(HttpSession session) {
        //移除本地 session
        session.removeAttribute(TOKEN_KEY);
        return "redirect:http://" + DOMAIN_NAME + ":8080/logout";
    }

    /**
     * NOTEME:session 其实是跟 JSESSIONID 绑定的！而 JSESSIONID 是存在 Cookie 里的。所以获取 session 其实就是去 key=JSESSIONID 的 concurrentHashMap 里获取 Value
     * {@See Request.doGetSession(boolean)}
     *
     * @param session
     * @return
     */
    @GetMapping("/removeSession")
    @ResponseBody
    public boolean removeSession(HttpSession session) {
        session.removeAttribute(TOKEN_KEY);
        log.info("remove session:{}", TOKEN_KEY);
        return true;
    }


    /**
     * 移除 client token
     *
     * @return
     */
    @GetMapping("/removeClientToken")
    @ResponseBody
    public boolean removeClientToken(@RequestParam("token") String token) {
        tokenMap.remove(token);
        return true;
    }

    public String getUserName(String token) throws IOException {
        String url = "http://" + DOMAIN_NAME + ":8080/getUserName?token=" + token;
        OkHttpClient client = new OkHttpClient().newBuilder()
                .build();
        Request request = new Request.Builder()
                .url(url)
                .method("GET", null)
                .build();
        Response response = client.newCall(request).execute();
        String json = response.body().string();
        log.info(json);
        return json;
    }

}
